package nachos.userprog;

import java.util.LinkedList;

import nachos.machine.*;
import nachos.threads.*;
import nachos.userprog.*;

/**
 * 一个可以支持多个用户进程的内核
 * <p>
 * A kernel that can support multiple user processes.
 */
public class UserKernel extends ThreadedKernel {
    /**
     * Allocate a new user kernel.
     */
    public UserKernel() {
	super();
    }

    /**
     * 初始化这个内核。创建一个同步控制台并且设置异常回调处理器。
     * Initialize this kernel. Creates a synchronized console and sets the
     * processor's exception handler.
     */
    public void initialize(String[] args) {
	super.initialize(args);

	console = new SynchConsole(Machine.console());
	/**************************************/
	freePageList = new LinkedList<Integer>();
	processList = new UserProcess[Machine.processor().getNumPhysPages()];
	processStatus = new int[Machine.processor().getNumPhysPages()];
	for(int i=0;i<processList.length;i++)
	{
		processList[i]=null;
		processStatus[i]=-1;
	}
	int page =  Machine.processor().getNumPhysPages();
	for(int i=0;i<page;i++)
	{

		freePageList.add(i);
	}
	sectionLock = new Lock();
	processAllocateLock = new Lock();
	//for(int i=0;)
	/**************************************/
	Machine.processor().setExceptionHandler(new Runnable() {
		public void run() { exceptionHandler(); }
	    });
    }
    public static int getFreePid()
    {
    	for(int i=0;i<processList.length;i++)
    	{
    		if(processList[i]==null)return i;
    	}
    	return -1;
    }
    /**
     * Test the console device.
     */	
    public void selfTest() {
	super.selfTest();

	System.out.println("Testing the console device. Typed characters");
	System.out.println("will be echoed until q is typed.");

	char c;

	do {
	    c = (char) console.readByte(true);
	    console.writeByte(c);
	}
	while (c != 'q');

	System.out.println("");
    }

    /**
     * Returns the current process.
     *
     * @return	the current process, or <tt>null</tt> if no process is current.
     */
    public static UserProcess currentProcess() {
	if (!(KThread.currentThread() instanceof UThread))
	    return null;
	
	return ((UThread) KThread.currentThread()).process;
    }

    /**
     * 异常处理函数。当用户指令发生cpu异常时，这个函数被调用。
     * <p>
     * (......)
     * The exception handler. This handler is called by the processor whenever
     * a user instruction causes a processor exception.
     *
     * <p>
     * When the exception handler is invoked, interrupts are enabled, and the
     * processor's cause register contains an integer identifying the cause of
     * the exception (see the <tt>exceptionZZZ</tt> constants in the
     * <tt>Processor</tt> class). If the exception involves a bad virtual
     * address (e.g. page fault, TLB miss, read-only, bus error, or address
     * error), the processor's BadVAddr register identifies the virtual address
     * that caused the exception.
     */
    public void exceptionHandler() {
	Lib.assertTrue(KThread.currentThread() instanceof UThread);

	UserProcess process = ((UThread) KThread.currentThread()).process;
	int cause = Machine.processor().readRegister(Processor.regCause);
	process.handleException(cause);
    }

    /**
     * 通过创建一个进程并且在里面运行shell程序，开始运行用户程序。shell程序的名称由
     * {@link Machine#getShellProgramName()}确定
     * Start running user programs, by creating a process and running a shell
     * program in it. The name of the shell program it must run is returned by
     * <tt>Machine.getShellProgramName()</tt>.
     *
     * @see	nachos.machine.Machine#getShellProgramName
     */
    public void run() {
	super.run();

	UserProcess process = UserProcess.newUserProcess();
	
	String shellProgram = Machine.getShellProgramName();	
	Lib.assertTrue(process.execute(shellProgram, new String[] { }));

	KThread.currentThread().finish();
    }

    /**
     * Terminate this kernel. Never returns.
     */
    public void terminate() {
	super.terminate();
    }

    /** Globally accessible reference to the synchronized console. */
    public static SynchConsole console;
    /******************************/
    public static LinkedList<Integer> freePageList = null;
    public static UserProcess[] processList = null;
    public static int[] processStatus = null;
    public static final String processFile = "process.info";
    public static void updateProcessFile()
    {
    	processAllocateLock.acquire();
    	StringBuilder sb = new StringBuilder();
    	String head = "pid\tppid\tstatus\tcommand\targs\n";
    	String format = "%3d\t%4d\t%2d\t%s\t%s\n";
    	int i=0;
    	sb.append(head);
    	
    	for(i=0;i<processList.length;i++)
    	{
    		String str = null;
    		if(processList[i]!=null)
    		{
    			UserProcess process = processList[i];
    			str = String.format(format, process.getPid(),process.getPPid(),1,process.name,"");
    		}
    		else if(processStatus[i]!=-1)
    		{
    			str = String.format(format, i,-1,processStatus[i],"dead","");
    		}
    		if(str!=null)sb.append(str);
    	}
    	sb.append("\n");
       	processAllocateLock.release();
       	OpenFile file = UserKernel.fileSystem.open(processFile, true);
       	byte[] buffer = sb.toString().getBytes();
       	file.write(buffer, 0, buffer.length);
       	byte[] b = {0};
       	file.write(b, 0, 1);
       	file.close();
    }
    
    /******************************/
    // dummy variables to make javac smarter
    private static Coff dummy1 = null;
    public static Lock sectionLock = null;
    public static Lock processAllocateLock = null;
}
